from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
from werkzeug.security import generate_password_hash
import os
from math import radians, sin, cos, sqrt, atan2, degrees
from datetime import datetime, timedelta

# 创建 Flask 应用
app = Flask(__name__)

# 数据库配置
HOSTNAME = "127.0.0.1"  # 数据库主机地址
PORT = 3306  # 数据库端口号
USERNAME = "root"  # 数据库用户名
PASSWORD = "123456"  # 数据库密码
DATABASE = "day59"  # 数据库名称

# 配置 SQLAlchemy 数据库 URI
app.config[
    'SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 禁用 SQLAlchemy 的修改跟踪

# 初始化 SQLAlchemy 数据库对象
db = SQLAlchemy(app)

# 测试数据库连接
with app.app_context():
    with db.engine.connect() as conn:
        rs = conn.execute(text("SELECT 1"))  # 执行简单查询以测试连接
        print("Database connection successful:", rs.fetchone())  # 打印测试结果


# 数据库模型
class User(db.Model):
    """用户模型"""
    __tablename__ = 'users'  # 表名
    id = db.Column(db.Integer, primary_key=True)  # 主键
    username = db.Column(db.String(50), unique=True)  # 用户名
    email = db.Column(db.String(100), unique=True)  # 邮箱
    password_hash = db.Column(db.String(128))  # 密码
    created_at = db.Column(db.DateTime, default=datetime.utcnow)  # 创建时间，默认为当前时间


class ScenicSpot(db.Model):
    """景点模型"""
    __tablename__ = 'scenic_spots'  # 表名
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(100), nullable=False)  # 景点名称
    description = db.Column(db.Text)  # 景点描述
    location = db.Column(db.String(200), nullable=False)  # 景点位置
    province = db.Column(db.String(50))  # 所在省份
    city = db.Column(db.String(50))  # 所在城市
    ticket_price = db.Column(db.Float)  # 门票价格
    opening_hours = db.Column(db.String(100))  # 开放时间
    best_season = db.Column(db.String(50))  # 最佳游览季节
    created_at = db.Column(db.DateTime, default=datetime.utcnow)  # 创建时间，默认为当前时间
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)  # 更新时间，默认为当前时间，更新时自动更新

    def to_dict(self):
        """将景点对象转换为字典主要原因是方便将其序列化为 JSON 格式"""
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}


class NavigationPoint(db.Model):
    """导航点模型"""
    __tablename__ = 'navigation_points'  # 表名
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(100), nullable=False)  # 导航点名称
    latitude = db.Column(db.Float, nullable=False)  # 纬度
    longitude = db.Column(db.Float, nullable=False)  # 经度
    spot_id = db.Column(db.Integer, db.ForeignKey('scenic_spots.id'))  # 关联的景点 ID
    created_at = db.Column(db.DateTime, default=datetime.utcnow)  # 创建时间，默认为当前时间

    def to_dict(self):
        """将导航点对象转换为字典"""
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}


# 创建数据库表
with app.app_context():
    db.create_all()  # 创建所有未创建的表


# 景点相关路由
@app.route('/scenic_spots', methods=['GET'])
def get_scenic_spots():
    """获取所有景点"""
    spots = ScenicSpot.query.all()  # 查询所有景点
    return jsonify([spot.to_dict() for spot in spots])  # 返回 JSON 格式的景点列表


@app.route('/scenic_spots/<int:spot_id>', methods=['GET'])
def get_scenic_spot(spot_id):
    """获取单个景点"""
    spot = ScenicSpot.query.get_or_404(spot_id)  # 根据 ID 查询景点，如果不存在则返回 404
    return jsonify(spot.to_dict())  # 返回 JSON 格式的景点信息


@app.route('/scenic_spots', methods=['POST'])
def create_scenic_spot():
    """创建新的景点"""
    data = request.get_json()  # 获取请求中的 JSON 数据

    if not data or not data.get('name') or not data.get('location'):
        return jsonify({'message': 'Missing required fields!'}), 400  # 如果缺少必要字段，返回 400 错误

    new_spot = ScenicSpot(
        name=data['name'],
        description=data.get('description'),
        location=data['location'],
        province=data.get('province'),
        city=data.get('city'),
        ticket_price=data.get('ticket_price', 0),
        opening_hours=data.get('opening_hours'),
        best_season=data.get('best_season')
    )  # 创建新的景点对象

    db.session.add(new_spot)  # 将新景点添加到会话
    db.session.commit()  # 提交会话，保存新景点到数据库

    return jsonify({'message': 'Scenic spot created!', 'spot': new_spot.to_dict()}), 201  # 返回成功消息和新景点信息


# 导航点相关路由
@app.route('/navigation_points', methods=['GET'])
def get_navigation_points():
    """获取所有导航点"""
    points = NavigationPoint.query.all()  # 查询所有导航点
    return jsonify([point.to_dict() for point in points])  # 返回 JSON 格式的导航点列表


@app.route('/navigation_points/<int:point_id>', methods=['GET'])
def get_navigation_point(point_id):
    """获取单个导航点"""
    point = NavigationPoint.query.get_or_404(point_id)  # 根据 ID 查询导航点，如果不存在则返回 404
    return jsonify(point.to_dict())  # 返回 JSON 格式的导航点信息


@app.route('/navigation_points', methods=['POST'])
def create_navigation_point():
    """创建新的导航点"""
    data = request.get_json()  # 获取请求中的 JSON 数据

    if not data or not data.get('name') or not data.get('latitude') or not data.get('longitude'):
        return jsonify({'message': 'Missing required fields!'}), 400  # 如果缺少必要字段，返回 400 错误

    new_point = NavigationPoint(
        name=data['name'],
        latitude=data['latitude'],
        longitude=data['longitude'],
        spot_id=data.get('spot_id')
    )  # 创建新的导航点对象

    db.session.add(new_point)  # 将新导航点添加到会话
    db.session.commit()  # 提交会话，保存新导航点到数据库

    return jsonify({'message': 'Navigation point created!', 'point': new_point.to_dict()}), 201  # 返回成功消息和新导航点信息


# 其他功能路由
@app.route('/scenic_spots/search', methods=['GET'])
def search_scenic_spots():
    """搜索景点"""
    query = request.args.get('q')  # 获取查询参数

    if not query:
        return jsonify({'message': 'Search query required!'}), 400  # 如果缺少查询参数，返回 400 错误

    results = ScenicSpot.query.filter(
        (ScenicSpot.name.ilike(f'%{query}%')) |  # 按景点名称搜索
        (ScenicSpot.location.ilike(f'%{query}%'))  # 按景点位置搜索
    ).all()

    return jsonify([spot.to_dict() for spot in results])  # 返回 JSON 格式的搜索结果


@app.route('/navigation/route', methods=['GET'])
def calculate_route():
    """计算导航路线"""
    start_id = request.args.get('start')  # 获取起始点 ID
    end_id = request.args.get('end')  # 获取终点 ID

    if not start_id or not end_id:
        return jsonify({'message': 'Start and end points required!'}), 400  # 如果缺少起始点或终点 ID，返回 400 错误

    start_point = NavigationPoint.query.get(start_id)  # 查询起始点
    end_point = NavigationPoint.query.get(end_id)  # 查询终点

    if not start_point or not end_point:
        return jsonify({'message': 'Point not found!'}), 404  # 如果起始点或终点不存在，返回 404 错误

    # 计算距离和方向（简化版）
    lat1, lon1 = radians(start_point.latitude), radians(start_point.longitude)
    lat2, lon2 = radians(end_point.latitude), radians(end_point.longitude)

    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    distance = 6371 * c  # 地球半径（公里）

    y = sin(dlon) * cos(lat2)
    x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon)
    bearing = (degrees(atan2(y, x)) + 360) % 360  # 方向角

    return jsonify({
        'distance_km': round(distance, 2),  # 距离（公里）
        'bearing_degrees': round(bearing, 2)  # 方向角（度）
    })


if __name__ == '__main__':
    app.run(debug=True)